package com.jian.socks5.tcp.handler;

import com.jian.socks5.common.Constants;
import com.jian.socks5.tcp.TCPBootstrap;
import com.jian.socks5.udp.UDPBootstrap;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.handler.codec.socksx.v5.*;

import java.net.InetSocketAddress;

/***
 * 处理socks5指令
 * @author Jian
 * @date 2021.06.22
 */
class Socks5CommandRequestHandler extends SimpleChannelInboundHandler<Socks5CommandRequest> {

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        System.err.println("Socks5CommandRequestHandler:" + cause.getMessage());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Socks5CommandRequest msg) throws Exception {
        System.out.print("请求命令类型:" + msg.type());

        String addr = msg.dstAddr();
        int port = msg.dstPort();
        String ipAndPort = addr + ":" + port;

        if (msg.type().equals(Socks5CommandType.CONNECT)) {

            System.out.println("，IP:" + ipAndPort);
            //连接目标机器
            bootstrapConnect(ctx, msg);
        }else if(msg.type().equals(Socks5CommandType.UDP_ASSOCIATE)){
            //System.err.println("UDP_ASSOCIATE");

            if (!Constants.udpServerIsStart.get()) {
                Bootstrap udpBootstrap = UDPBootstrap.newSocks5DefInstance();
                ChannelFuture bind = udpBootstrap.bind(Constants.localUdpPort);
                bind.addListener(future -> {
                    ChannelFuture channelFuture = (ChannelFuture)future;
                    DefaultSocks5CommandResponse defaultSocks5CommandResponse;
                    if (future.isSuccess()) {
                        Constants.udpServerIsStart.set(true);
                        InetSocketAddress socketAddress = (InetSocketAddress)channelFuture.channel().localAddress();
                        System.out.println("UDP Server启动完成！端口：" + socketAddress.getPort());
                        defaultSocks5CommandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Constants.localUdpAddrType, Constants.localUdpAddr, socketAddress.getPort());
                        ctx.writeAndFlush(defaultSocks5CommandResponse);
                        //启动完成后添加客户端能接收来自该服务端IP和端口数据白名单
                        Constants.ipAndPorts.add(ipAndPort);
                    }else{
                        System.err.println("UDP Server启动失败！");
                        defaultSocks5CommandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, Socks5AddressType.IPv4, "0.0.0.0", 0);
                        ChannelFuture chFu = ctx.writeAndFlush(defaultSocks5CommandResponse);
                        chFu.addListener(ChannelFutureListener.CLOSE);
                    }
                });
            }else{
                DefaultSocks5CommandResponse defaultSocks5CommandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, Constants.localUdpAddrType, Constants.localUdpAddr, Constants.localUdpPort);
                ctx.writeAndFlush(defaultSocks5CommandResponse);
                Constants.ipAndPorts.add(ipAndPort);
            }


        }else if(msg.type().equals(Socks5CommandType.BIND)){
            System.err.println("BIND");
        }
    }



    public void bootstrapConnect(ChannelHandlerContext ctx, Socks5CommandRequest msg) {

        String addr = msg.dstAddr();
        int port = msg.dstPort();

        InetSocketAddress inetSocketAddress = new InetSocketAddress(addr, port);
        Bootstrap localhost = TCPBootstrap.newDefBootstrap(inetSocketAddress);
        String ipAndPort = addr + ":" + port;
        System.out.println("连接" + ipAndPort + "中...");
        ChannelFuture destChannelFuture = localhost.connect();

        //连接结果监听
        destChannelFuture.addListener(future -> {
            DefaultSocks5CommandResponse defaultSocks5CommandResponse;
            if (future.isSuccess()) {
                System.out.println("连接" + ipAndPort + "完成！");
                defaultSocks5CommandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.SUCCESS, msg.dstAddrType(), addr, port);
                ctx.writeAndFlush(defaultSocks5CommandResponse);

                Channel clientChannel = destChannelFuture.channel();

                //添加连接信息--目标机器
                Channel ctxChannel = ctx.channel();
                ctxChannel.attr(Constants.CLIENT_CHANNEL_ATTR).set(clientChannel);
                ctxChannel.attr(Constants.CHANNEL_ATTR_IPINFO).set(ipAndPort);

                //添加连接信息--客户机器
                clientChannel.attr(Constants.DEST_CHANNEL_ATTR).set(ctx);
                clientChannel.attr(Constants.CHANNEL_ATTR_IPINFO).set(ipAndPort);

                //添加客户机器的后续数据发送
                ctx.pipeline().addLast(Constants.client2ServerHandler);

            } else { //已连接
                System.err.println("连接" + ipAndPort + "失败！");
                defaultSocks5CommandResponse = new DefaultSocks5CommandResponse(Socks5CommandStatus.FAILURE, msg.dstAddrType(), addr, port);
                ChannelFuture channelFuture = ctx.writeAndFlush(defaultSocks5CommandResponse);
                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
        });
    }



}
